package com.gupaoedu.mvcframework.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.gupaoedu.demo.service.impl.DemoService;
import com.gupaoedu.mvcframework.annotation.GPAutowried;
import com.gupaoedu.mvcframework.annotation.GPController;
import com.gupaoedu.mvcframework.annotation.GPRequestMapping;
import com.gupaoedu.mvcframework.annotation.GPRequestParam;
import com.gupaoedu.mvcframework.annotation.GPService;
import com.sun.javafx.scene.layout.region.Margins.Converter;

import junit.framework.Protectable;


public class GPDispatcherServlet extends HttpServlet{
	
	private Properties p=new Properties();
	private List<String> classNames=new ArrayList<String>();
	private Map<String, Object> ioc = new HashMap<String, Object>();
//	private Map<String, Method> handlerMapping = new HashMap<String, Method>();
	private List<Handler> handlerMapping = new ArrayList<Handler>();
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		try {
			doDispatch(req,resp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		try {
			doDispatch(req,resp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		//1.加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));
		//2.扫描所有的类
		doScanner(p.getProperty("scanPackage"));
		//3.初始化所有相关Class的实例,并且将其保存到Ioc容器中
		doInstance();
		//4.自动化DI依赖注入
		doAutowierd();
		//5.初始化HandlerMapping
		initHandlerMapping();
		System.out.println("MVC Frame is init");
	}
	
	/**
	 * @param location: web.xml spring的配置文件
	 */
	private void doLoadConfig(String location){
		InputStream is= this.getClass().getClassLoader().getResourceAsStream(location);
		try {
			p.load(is);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if (is!=null) {
				try {
					is.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	};
	
	private void doScanner(String packageName){
		//这句有疑惑
		URL url = this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
		File classDir = new File(url.getFile());
		//扫描此路径下的所有文件夹或文件
		for(File file : classDir.listFiles()){
			if (file.isDirectory()) {
				doScanner(packageName+"."+file.getName());
			}else{
				String className = packageName+"."+file.getName().replace(".class", "");
				classNames.add(className);
			}
		}
	};
	
	private void doInstance(){
		if(classNames.isEmpty()){
			return;
		}
		try {
			for(String className: classNames){
				//反射初始化对象
				Class<?> clazz = Class.forName(className);
				//进行实例化,原则问题
				//判断,不是所有的牛奶都叫特仑苏
				if (clazz.isAnnotationPresent(GPController.class)) {
					//beanName beanId
					String beanName = lowFirst(clazz.getSimpleName());
					ioc.put(beanName, clazz.newInstance());
				}else if(clazz.isAnnotationPresent(GPService.class)){
					//beanName beanId
					//1.默认采用类名的首字母小写
					//2.如果自己定义了名字的话,优先使用自己定义的名字
					//3.根据类型匹配,利用接口作为key
					GPService service=clazz.getAnnotation(GPService.class);
					String beanName = service.value();
					if ("".equals(beanName.trim())) {
						beanName=lowFirst(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);
					Class<?>[] interfaces = clazz.getInterfaces();
					//按类型匹配
					for(Class<?> i:interfaces){
						ioc.put(i.getName(), instance);
					}
					
					
				}else{
					continue;
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	};
	
	private void doAutowierd(){
		if (ioc.isEmpty()) {
			return;
		}
		for(Entry<String, Object> entry:ioc.entrySet()){
			//在spring里面没有隐私,哪怕是private
			Field[] fields=entry.getValue().getClass().getDeclaredFields();
			for(Field field:fields){
				if (!field.isAnnotationPresent(GPAutowried.class)) {
					continue;
				}
				GPAutowried autowried=field.getAnnotation(GPAutowried.class);
				String beanName = autowried.value().trim();
				if ("".equals(beanName)) {
					beanName=field.getType().getName();
				}
				
				field.setAccessible(true);//暴利访问 作用就是让我们在用反射时访问私有变量
				try {
					System.out.println(entry.getValue());
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
					continue;
				}
			}
		}
	};
	
	private void initHandlerMapping(){
		if (ioc.isEmpty()) {
			return;
		}
		for (Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			if (!clazz.isAnnotationPresent(GPRequestMapping.class)) {
				continue;
			}
			String url = "";
			if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
				GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
				url = requestMapping.value();
			}
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (!method.isAnnotationPresent(GPRequestMapping.class)) {continue;}
					GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
					//例：/code/list
//					String murl = url+requestMapping.value();
//					handlerMapping.put(murl, method);
//					System.out.println("Mapping : "+ murl +"  "+method);
					String regex = ("/"+url+requestMapping.value()).replaceAll("/+", "/");
					Pattern pattern=Pattern.compile(regex);
					handlerMapping.add(new Handler(entry.getValue(), method, pattern));
					System.out.println("Mapping"+regex+","+method);
				
			}
		}
		
		
	};
	
	private void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception{
//		String url=request.getRequestURI();
//		//根路径
//		String contextPath = request.getContextPath();
//		url=url.replace(contextPath, "").replaceAll("/+", "/");//可能会多个/  /斜线
//		if (!handlerMapping.containsKey(url)) {
//			response.getWriter().write("404 Not Found");
//			return;
//		}
//		System.out.println("获得对应的方法:"+handlerMapping.get(url));
		Handler handler = getHandler(request);
		if (handler==null) {
			response.getWriter().write("404 Not Found");
			return;
		}
		//保存所有的参数列表
		Class<?>[] paramTypes = handler.method.getParameterTypes();
		//保存所有需要自动赋值的参数值
		Object[] paramValues = new Object[paramTypes.length];
		Map<String, String[]> params =request.getParameterMap();//key:参数名   value:form表单值
		for (Entry<String, String[]> param: params.entrySet()) {
			String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "");
			//如果找到匹配对象,则开始填充参数
			if (!handler.paramIndexMapping.containsKey(param.getKey())) {
				continue;
			}
			int index = handler.paramIndexMapping.get(param.getKey());
			paramValues[index] = convert(paramTypes[index],value);
		}
		//设置方法中的request和resPonse对象
		int reqIndex= handler.paramIndexMapping.get(HttpServletRequest.class.getName());
		paramValues[reqIndex] = request;
		int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
		paramValues[respIndex] = response;
		//invoke的参数获取是难点
		handler.method.invoke(handler.controller, paramValues);
	}
	
	private String lowFirst(String str){
		char[] chars=str.toCharArray();
		chars[0] +=32;
		return String.valueOf(chars);
	}
	
	private Handler getHandler(HttpServletRequest request) throws Exception{
		if (handlerMapping.isEmpty()) {
			return null;
		}
		String url=request.getRequestURI();
		String contextPath = request.getContextPath();
		url=url.replace(contextPath, "").replaceAll("/+", "/");//可能会多个/  /斜线
		for (Handler  handler: handlerMapping) {
			try {
				Matcher matcher = handler.pattern.matcher(url);
				//如果没有匹配上继续下一个匹配
				if (!matcher.matches()) {
					continue;
				}
				return handler;
			} catch (Exception e) {
				// TODO: handle exception
				throw e;
			}
		}
		return null;
	}
	
	/**
	 * @param type 赋值类型转换函数
	 * @param value
	 * @return
	 */
	private Object convert(Class<?> type,String value){
		if (Integer.class==type) {
			return Integer.valueOf(value);
		}
		return value;
	}
	
	/**Handler记录了controller中的RequestMapping和Method的对应关系
	 * @author Administrator
	 *
	 */
	private class Handler{
		
		protected Object controller;//保存方法对应的实例
		protected Method method;//保存隐射方法
		protected Pattern pattern;
		protected Map<String, Integer> paramIndexMapping;//参数顺序 难点
		
		public Handler(Object controller, Method method, Pattern pattern) {
			super();
			this.controller = controller;
			this.method = method;
			this.pattern = pattern;
			this.paramIndexMapping = new HashMap<String, Integer>();
			//对Method方法参数提取
			putParamIndexMapping(method);
		}
		
		/**
		 * @param method Handler记录Controller中的RequestMapping和Method的对应关系
		 */
		private void putParamIndexMapping(Method method){
			
			//提取方法中加了注解的参数
			Annotation [][] pa=method.getParameterAnnotations();
			for (int i = 0; i < pa.length; i++) {
				for (Annotation annotations : pa[i]) {
					if (annotations instanceof GPRequestParam) {
						String paramName=((GPRequestParam) annotations).value();
						if (!"".equals(paramName.trim())) {
							paramIndexMapping.put(paramName, i);
						}
					}
				}
			}
			
			Class<?>[] paramTypes = method.getParameterTypes();
			for (int i = 0; i < paramTypes.length; i++) {
				Class<?> type = paramTypes[i];
				if (type==HttpServletRequest.class||type==HttpServletResponse.class) {
					paramIndexMapping.put(type.getName(), i);
				}
			}
		}
	
	}
}


